package cn.bird.storm.config;

import cn.bird.storm.component.CollectPathAdapter;
import cn.bird.storm.component.CollectPathComponent;
import cn.bird.storm.model.bo.CollectPathProperties;
import cn.bird.storm.model.bo.RequestMappingBO;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Slf4j
@AutoConfiguration
@EnableConfigurationProperties(CollectPathProperties.class)
public class CollPathConfigure {

    @Bean
    @ConditionalOnProperty(prefix = "storm.coll.path", name = "enable", havingValue = "true")
    public CollectPathComponent collectPathComponent(CollectPathAdapter collectPathAdapter, CollectPathProperties collectPathProperties) {
        return new CollectPathComponent(collectPathAdapter, collectPathProperties);
    }

    @Bean
    @ConditionalOnMissingBean(CollectPathAdapter.class)
    @ConditionalOnProperty(prefix = "storm.coll.path",name = "cache",havingValue = "local")
    public CollectPathAdapter collectPathAdapter() {
        Cache<String, List<RequestMappingBO>> cache = Caffeine.newBuilder().initialCapacity(10).maximumSize(100).build();
        return new CollectPathAdapter() {
            @Override
            public void needAuthSave(String key, List<RequestMappingBO> bos) {
                log.info("need auth save");
                cache.put(key, bos);
            }

            @Override
            public void noNeedAuthSave(String key, List<RequestMappingBO> bos) {
                log.info("no need auth save");
                cache.put(key, bos);
            }

            @Override
            public List<RequestMappingBO> needAuthGet(String key) {
                log.info("need auth get");
                return cache.get(key, k -> new ArrayList<>());
            }

            @Override
            public List<RequestMappingBO> noNeedAuthGet(String key) {
                log.info("no need auth get");
                return cache.get(key, k -> new ArrayList<>());
            }

        };
    }

    @Bean
    @ConditionalOnMissingBean(CollectPathAdapter.class)
    @ConditionalOnProperty(prefix = "storm.coll.path",name = "cache",havingValue = "redis")
    public CollectPathAdapter redisPathAdapter(RedisTemplate<String, Object> redisTemplate,
                                               ObjectMapper objectMapper) {
        return new CollectPathAdapter() {
            @Override
            public void needAuthSave(String key, List<RequestMappingBO> bos) {
                log.info("need auth save");
                redisTemplate.opsForValue().set(key, bos);
            }

            @Override
            public void noNeedAuthSave(String key, List<RequestMappingBO> bos) {
                log.info("no need auth save");
                redisTemplate.opsForValue().set(key, bos);
            }

            @Override
            public List<RequestMappingBO> needAuthGet(String key) {
                log.info("need auth get");
                Object o = redisTemplate.opsForValue().get(key);
                if (Objects.isNull(o)) {
                    return new ArrayList<>();
                }
                JavaType javaType = getCollectionType(objectMapper, ArrayList.class, RequestMappingBO.class);
                return objectMapper.convertValue(o, javaType);

            }

            @Override
            public List<RequestMappingBO> noNeedAuthGet(String key) {
                log.info("no need auth get");
                Object o = redisTemplate.opsForValue().get(key);
                if (Objects.isNull(o)) {
                    return new ArrayList<>();
                }
                JavaType javaType = getCollectionType(objectMapper, ArrayList.class, RequestMappingBO.class);
                return objectMapper.convertValue(o, javaType);
            }
        };
    }

    /**
     * 获取泛型的Collection Type
     * @param collectionClass 泛型的Collection
     * @param elementClasses 元素类
     * @return JavaType Java类型
     * @since 1.0
     */
    public JavaType getCollectionType(ObjectMapper objectMapper,Class<?> collectionClass, Class<?>... elementClasses) {
        return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }


}
